<?php

namespace yifang;

use yifang\container\Container;
use  yifang\utils\Str;

use yifang\initializer\RegisterService;
use yifang\initializer\BootService;
use yifang\initializer\Error;

use yifang\lib\Event;
use yifang\lib\Request;
use yifang\lib\Http;
use yifang\lib\Env;
use yifang\lib\Config;
use yifang\lib\Lang;
use yifang\lib\Helper;
use yifang\lib\Middleware;
use yifang\lib\Route;
use yifang\lib\Db;
use yifang\lib\Data;
use yifang\lib\Log;
use yifang\lib\Cookie;
use yifang\lib\View;
use yifang\response\Template;

use yifang\event\AppInit;


/**
 * 应用
 */
class App extends Container
{
    const VERSION = '1.0.0';

    /**
     * 初始化
     * @var bool
     */
    protected $initialized = false;

    /**
     * 应用开始时间
     * @var float
     */
    protected $beginTime;

    /**
     * 应用内存初始占用
     * @var integer
     */
    protected $beginMem;

    /**
     * 当前应用类库命名空间
     * @var string
     */
    protected $namespace = 'app';

    /**
     * 应用根目录
     * @var string
     */
    protected $rootPath = '';

    /**
     * 框架目录
     * @var string
     */
    protected $yifangPath = '';

    /**
     * 应用目录
     * @var string
     */
    protected $appPath = '';

    /**
     * Runtime目录
     * @var string
     */
    protected $runtimePath = '';

    /**
     * 路由定义目录
     * @var string
     */
    protected $routePath = '';

    /**
     * 应用调试模式
     * @var bool
     */
    protected $appDebug = false;

    /**
     * 环境变量标识
     * @var string
     */
    protected $envName="";

    /**
     * 应用名称
     * @var string
     */
    protected $appName="";

    /**
     * 配置后缀
     * @var string
     */
    protected $configExt = '.php';
    /**
     * 应用初始化器
     * @var array
     */
    protected $initializers = [
        Error::class,
        RegisterService::class,
        BootService::class,
    ];

    /**
     * 注册的系统服务
     * @var array
     */
    protected $services = [];

    /**
     * 容器绑定标识
     * @var array
     */
    protected $bind = [
        'app'                     => App::class,
        'event'                   => Event::class,
        'request'                   => Request::class,
        'http'                   => Http::class,
        'env'                   => Env::class,
        'config'                   => Config::class,
        'lang'                   => Lang::class,
        'middleware'                   => Middleware::class,
        'route'                   => Route::class,
        'cookie'                   => Cookie::class,
        'db'         => Db::class,
        'data'         => Data::class,
        'log'         => Log::class,
        'view'         => View::class,
        'template'         => Template::class,
    ];

    /**
     * 架构方法
     * @access public
     * @param string $rootPath 应用根目录
     */
    public function __construct($rootPath = '')
    {
        $this->yifangPath   = realpath(__DIR__) . DIRECTORY_SEPARATOR;
        $this->rootPath    = $rootPath ? rtrim($rootPath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR : $this->getDefaultRootPath();
        $this->appPath     = $this->rootPath . 'app' . DIRECTORY_SEPARATOR;
        $this->runtimePath = $this->rootPath . 'runtime' . DIRECTORY_SEPARATOR;

        /**
         * 应用服务绑定
         */
        $provider=$this->appPath . 'container.php';
        if (is_file($provider)) {
            $this->bind(include $provider);
        }

        static::setInstance($this);
        $this->instance("app", $this);
        $this->instance(Container::class, $this);
    }

    /**
     * 获取应用根目录
     * @access public
     * @return string
     */
    public function getRootPath()
    {
        return $this->rootPath;
    }

    /**
     * 获取框架版本
     * @access public
     * @return string
     */
    public function version()
    {
        return static::VERSION;
    }

    /**
     * 获取应用框架目录
     * @access public
     * @return string
     */
    public function getYifangPath()
    {
        return $this->yifangPath;
    }

    /**
     * 获取运行目录
     * @access public
     * @return string
     */
    public function getRuntimePath()
    {
        return $this->runtimePath;
    }

    /**
     * 设置runtime目录
     * @param string $path 定义目录
     */
    public function setRuntimePath($path)
    {
        $this->runtimePath = $path;
    }

    /**
     * 获取自定义配置目录
     * @access public
     * @return string
     */
    public function getCustomPath()
    {
        return $this->getRootPath()."custom".DIRECTORY_SEPARATOR;
    }

    /**
     * 加载目录
     */
    public function loadConfigDir(){
        $dir=array();
        $dir[]=$this->getAppPath()."config".DIRECTORY_SEPARATOR;
        return $dir;
    }

    /**
     * 加载目录
     */
    public function loadConfigCustomDir(){
        $dir=array();
        $dir[]=$this->getCustomPath()."config".DIRECTORY_SEPARATOR;
        return $dir;
    }

    /**
     * 引导应用
     * @access public
     * @return void
     */
    public function boot()
    {
        array_walk($this->services, function ($service) {
            $this->bootService($service);
        });
    }

    /**
     * 执行服务
     */
    public function bootService($service)
    {
        if (method_exists($service, 'boot')) {
            return $this->invoke([$service, 'boot']);
        }
    }

    /**
     * 获取服务
     */
    public function getService($service)
    {
        $name = is_string($service) ? $service : get_class($service);
        return array_values(array_filter($this->services, function ($value) use ($name) {
            return $value instanceof $name;
        }, ARRAY_FILTER_USE_BOTH))[0] ?? null;
    }

    /**
     * 注册服务
     */
    public function register($service, $force = false)
    {
        $registered = $this->getService($service);
        if ($registered && !$force) {
            return $registered;
        }
        if (is_string($service)) {
            $service = new $service($this);
        }
        if (method_exists($service, 'register')) {
            $service->register();
        }
        if (property_exists($service, 'bind')) {
            $this->bind($service->bind);
        }
        $this->services[] = $service;
    }

    /**
     * 初始化
     */
    public function init()
    {
        if(!$this->initialized()){
            $this->yifangPath   = realpath(__DIR__) ."/";
            $this->rootPath    = $this->getDefaultRootPath();
            $this->appPath     = $this->rootPath . 'app/';
            $this->runtimePath = $this->rootPath . 'runtime/';
            $this->routePath = $this->appPath . 'route/';

            //初始化应用
            try{
                $this->initialize();
            }catch (\Throwable $e){
                throw  $e;
            }
        }
        return $this;
    }

    /**
     * 解析应用类的类名
     * @access public
     * @param string $layer 层名 controller model ...
     * @param string $name  类名
     * @return string
     */
    public function parseClass($layer, $name,$namespace="")
    {
        $name  = str_replace(['/', '.'], '\\', $name);
        $array = explode('\\', $name);
        $class = array_pop($array);
        $path  = $array ? implode('\\', $array) . '\\' : '';
        if(!$namespace){
            return  $this->namespace. '\\' . $layer . '\\' . $path . $class;
        }else{
            return  $namespace .'\\' . $class;
        }
    }

    /**
     * 设置应用命名空间
     */
    public function setNamespace($namespace)
    {
        $this->namespace = $namespace;
        return $this;
    }

    /**
     * 获取应用类库命名空间
     */
    public function getNamespace()
    {
        return $this->namespace;
    }

    /**
     * 设置环境变量标识
     * @access public
     * @param string $name 环境标识
     * @return $this
     */
    public function setEnvName($name)
    {
        $this->envName = $name;
        return $this;
    }

    /**
     * 设置应用名称
     * @access public
     * @param string $name 环境标识
     * @return $this
     */
    public function setAppName($name)
    {
        $this->appName = $name;
        return $this;
    }

    /**
     * 是否初始化过
     * @return bool
     */
    public function initialized()
    {
        return $this->initialized;
    }

    /**
     * 初始化应用
     */
    public function initialize(){
        $this->initialized = true;

        $this->beginTime = microtime(true);
        $this->beginMem  = memory_get_usage();

        //加载环境文件
        $this->loadEnv($this->envName);

        $this->configExt = $this->env->get('config_ext', '.php');

        //设置调试模式
        $this->debugModeInit();

        // 加载全局初始化文件
        $this->load();

        // 加载系统语言包
        $langSet = $this->lang->defaultLangSet();
        $this->lang->load([
            $this->app->getYifangPath() . 'lang' . DIRECTORY_SEPARATOR . $langSet . '.php',
        ]);
        // 加载应用默认语言包
        $this->loadLangPack();

        // 监听AppInit
        $this->event->trigger(AppInit::class);

        //设置时区
        date_default_timezone_set($this->config->get('app.default_timezone', 'Asia/Shanghai'));

        //设置编码
        header("Content-Type:text/html;charset=".$this->config->get('app.charset', 'utf-8'));

        // 系统初始化
        foreach ($this->initializers as $initializer) {
            $this->make($initializer)->init($this);
        }
    }

    /**
     * 加载语言包
     * @return void
     */
    public function loadLangPack()
    {
        // 加载默认语言包
        $langSet = $this->lang->defaultLangSet();
        $this->lang->switchLangSet($langSet);
    }

    /**
     * 加载环境变量定义
     */
    public function loadEnv($envName = '')
    {
        // 加载环境变量
        $envFile = $envName ? $this->rootPath . '.env.' . $envName : $this->rootPath . '.env';
        if (is_file($envFile)) {
            $this->env->load($envFile);
        }
    }

    /**
     * 获取当前应用目录
     * @access public
     * @return string
     */
    public function getAppPath()
    {
        return $this->appPath;
    }

    /**
     * 获取应用基础目录
     * @access public
     * @return string
     */
    public function getBasePath()
    {
        return $this->rootPath . 'app' . DIRECTORY_SEPARATOR;
    }

    /**
     * 设置应用目录
     * @param string $path 应用目录
     */
    public function setAppPath($path)
    {
        $this->appPath = $path;
    }

    /**
     * 获取应用配置目录
     */
    public function getConfigPath()
    {
        return $this->rootPath . 'config' . DIRECTORY_SEPARATOR;
    }

    /**
     * 获取配置后缀
     */
    public function getConfigExt()
    {
        return $this->configExt;
    }

    /**
     * 注册应用事件
     */
    public function loadEvent(array $event)
    {
        if (isset($event['bind'])) {
            $this->event->bind($event['bind']);
        }
        if (isset($event['listen'])) {
            $this->event->listenEvents($event['listen']);
        }
        if (isset($event['subscribe'])) {
            $this->event->subscribe($event['subscribe']);
        }
    }

    /**
     * 加载应用文件和配置
     * @access protected
     * @return void
     */
    protected function load()
    {
        //加载助手函数
        Helper::loader();

        //配置加载
        $this->config->loader($this->loadConfigDir());

        //融合自定义配置
        $this->config->loader($this->loadConfigCustomDir(),2);

        //加载常量文件
        $constantsFile=$this->yifangPath."/lib/Constants.php";
        if(file_exists($constantsFile)){
            @include_once $constantsFile;
        }

        //加载系统事件
        $event_file=$this->yifangPath."config".DIRECTORY_SEPARATOR."event".$this->getConfigExt();
        if (is_file($event_file)) {
            $even_sys_data=$this->config->parse($event_file);
            $this->loadEvent($even_sys_data);
        }

        //加载应用事件
        $this->event->loader();

        //加载服务
        $services = config("provider",array());
        foreach ($services as $service) {
            $this->register($service);
        }
    }

    /**
     * 执行应用程序
     */
    public function run()
    {
        try{
            $response = router()->dispatch(request());
            return $response;
        }catch (\Throwable $e){
            throw  $e;
        }
    }

    /**
     * 执行cmd应用程序
     */
    public function cmd_run()
    {
        // 执行应用并响应
        app("console")->run();
    }

    /**
     * 调试模式设置
     * @access protected
     * @return void
     */
    protected function debugModeInit()
    {
        // 应用调试模式
        if (!$this->appDebug) {
            $this->appDebug = $this->env->get('app_debug') ? true : false;
            ini_set('display_errors', "Off");
        }
    }

    /**
     * 开启应用调试模式
     */
    public function debug($debug = true)
    {
        $this->appDebug = $debug;
        return $this;
    }

    /**
     * 是否为调试模式
     */
    public function isDebug()
    {
        return $this->appDebug;
    }

    /**
     * 是否运行在命令行下
     * @return bool
     */
    public function runningInConsole()
    {
        return php_sapi_name() === 'cli' || php_sapi_name() === 'phpdbg';
    }

    /**
     * 获取应用根目录
     * @access protected
     * @return string
     */
    protected function getDefaultRootPath()
    {
        return dirname($this->yifangPath, 1) . DIRECTORY_SEPARATOR;
    }
}